using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using SolanumSolstice_Windows.Mapping;
using SolanumSolstice_Windows.Units.Weapons;

namespace SolanumSolstice_Windows.Units
{
    public class Unit : ICloneable
    {
        //Unit Stats
        protected int hitPoints;
        protected int maxHitPoints;
        protected float recoveryTime;
        protected float initialRecoveryTime;
        protected Vector2 position;
        protected float velocity;
        protected int sightRadius;
        protected float attackRadius;
        protected int attackPower;
        protected UnitClass unitClass;
        protected Weapon weapon;
        protected int spawnCost;
        protected int timeCost;

        //Logic
        protected bool isDead;
        protected bool isDying;
        protected bool inBuilding;
        protected bool isAttacking;
        protected bool isSelected;
        protected bool isZombie;
        protected bool actionSelected;
        protected bool actionReady;
        protected Random rand;

        protected Vector2 tempDirection;

        //Actions
        protected List<Action> actions;
        protected int currentActionIndex;


        //Pathfinding
        protected Vector2 targetPosition;
        protected Vector2 finalTarget;
        protected bool targetReached;
        protected bool dealingDamage;
        protected int dealingDamageCounter;
        protected Unit zTarget;
        protected Path path;
        protected int pathIndex;

        //Graphics
        protected Vector2 direction;
        protected Texture2D texture;
        protected Texture2D hudTexture;
        protected Texture2D selectedCircle;
        protected Texture2D emptyBar;
        protected Texture2D fullBar;
        protected int frameIncrementer;
        protected int frameCount;
        protected int weaponFrameIncrementer;
        protected int weaponFrameCount;
        protected int dyingFrameCount;
        protected Vector2 groupOffset;

        protected ContentManager content;











        #region Accessors

        public Vector2 Position
        {
            get
            {
                return position;
            }
            set
            {
                position = value;
            }
        }

        public Texture2D HUDTexture
        {
            get
            {
                return hudTexture;
            }
            set
            {
                hudTexture = value;
            }
        }

        public int SightRadius
        {
            get
            {
                return sightRadius;
            }
            set
            {
                sightRadius = value;
            }
        }

        public float AttackRadius
        {
            get
            {
                return attackRadius;
            }
            set
            {
                attackRadius = value;
            }
        }

        public bool IsSelected
        {
            get
            {
                return isSelected;
            }
            set
            {
                isSelected = value;
            }
        }

        public bool TargetReached
        {
            get
            {
                return targetReached;
            }
            set
            {
                targetReached = value;
            }
        }

        public bool ActionSelected
        {
            get
            {
                return actionSelected;
            }
            set
            {
                actionSelected = value;
            }
        }

        public Vector2 GroupOffset
        {
            get
            {
                return groupOffset;
            }
            set
            {
                groupOffset = value;
                position += groupOffset;
            }
        }

        public List<Action> Actions
        {
            get
            {
                return actions;
            }
            set
            {
                actions = value;
            }
        }

        public int CurrentActionIndex
        {
            get
            {
                return currentActionIndex;
            }
            set
            {
                currentActionIndex = value;
            }
        }

        public bool IsDead
        {
            get
            {
                return isDead;
            }
            set
            {
                isDead = value;
            }
        }

        public bool IsZombie
        {
            get
            {
                return isZombie;
            }
            set
            {
                isZombie = value;
            }
        }

        public bool InBuilding
        {
            get
            {
                return inBuilding;
            }
            set
            {
                inBuilding = value;
            }
        }


        public int AttackPower
        {
            get
            {
                return attackPower;
            }
            set
            {
                attackPower = value;
            }
        }

        public float RecoveryTime
        {
            get
            {
                return recoveryTime;
            }
            set
            {
                recoveryTime = value;
            }
        }

        public float InitialRecoveryTime
        {
            get
            {
                return initialRecoveryTime;
            }
            set
            {
                initialRecoveryTime = value;
            }
        }



        public bool ActionReady
        {
            get
            {
                return actionReady;
            }
            set
            {
                actionReady = value;
            }
        }

        public UnitClass UnitClass
        {
            get
            {
                return unitClass;
            }
            set
            {
                unitClass = value;
            }
        }

        public int HitPoints
        {
            get
            {
                return hitPoints;
            }
            set
            {
                hitPoints = value;
            }
        }

        public int MaxHitPoints
        {
            get
            {
                return maxHitPoints;
            }
            set
            {
                maxHitPoints = value;
            }
        }

        public bool IsAttacking
        {
            get
            {
                return isAttacking;
            }
            set
            {
                isAttacking = false;
            }
        }

        public Weapon Weapon
        {
            get
            {
                return weapon;
            }
            set
            {
                weapon = value;
                attackRadius = weapon.AttackRadius;
            }
        }

        public int SpawnCost
        {
            get
            {
                return spawnCost;
            }
            set
            {
                spawnCost = value;
            }
        }

        public int TimeCost
        {
            get
            {
                return timeCost;
            }
            set
            {
                timeCost = value;
            }
        }
        #endregion

        //the "right" way
        public virtual Object Clone()
        {
            Unit u = new Unit(this.content, this.position);

            u.weapon = new Fist(this.content);
            u.hitPoints = this.hitPoints;
            u.maxHitPoints = this.maxHitPoints;
            u.recoveryTime = this.recoveryTime;
            u.initialRecoveryTime = this.initialRecoveryTime;
            u.position = this.position;
            u.velocity = this.velocity;
            u.sightRadius = this.sightRadius;
            u.attackRadius = this.attackRadius;
            u.attackPower = this.attackPower;

            u.timeCost = this.timeCost;
            u.spawnCost = this.spawnCost;

            u.isSelected = this.isSelected;
            u.isDead = this.isDead;
            u.isDying = this.isDying;
            u.isZombie = this.isZombie;
            u.inBuilding = this.inBuilding;
            u.isAttacking = this.isAttacking;
            u.actionSelected = this.actionSelected;
            u.actionReady = this.actionReady;
            u.rand = new Random();
            u.actions = new List<Action>();
            foreach (Action a in this.actions)
            {
                u.actions.Add(a);
            }
            u.currentActionIndex = this.currentActionIndex;
            u.targetReached = this.targetReached;
            u.direction = new Vector2(this.direction.X, this.direction.Y);
            /*u.texture = content.Load<Texture2D>("Content\\Txtr\\Units\\Sprites\\civ_civilian_sheet");
            u.hudTexture = content.Load<Texture2D>("Content\\Txtr\\Units\\HUD\\copHUD");
            u.selectedCircle = content.Load<Texture2D>("Content\\Txtr\\Units\\selectedUnitCircle");
            u.emptyBar = content.Load<Texture2D>("Content\\Txtr\\Units\\EmptyBar");
            u.fullBar = content.Load<Texture2D>("Content\\Txtr\\Units\\FullBarWhite");*/
            u.frameCount = this.frameCount;
            u.frameIncrementer = this.frameIncrementer;
            u.weaponFrameCount = this.weaponFrameCount;
            u.dyingFrameCount = this.dyingFrameCount;
            u.groupOffset = new Vector2(this.groupOffset.X, this.groupOffset.Y);

            return (Object)u;
        }

        //the cheap way 
        /*   public override Unit Clone()
           {
               return new Unit(this.content, this.position);
           }*/

        public Unit(ContentManager content, Vector2 position)
        {
            weapon = new Fist(content);

            this.hitPoints = 10;
            this.maxHitPoints = hitPoints;
            recoveryTime = 1;
            initialRecoveryTime = 1;
            this.position = position;
            velocity = 1.0f;
            sightRadius = 5;
            attackRadius = weapon.AttackRadius;
            attackPower = 1;


            isSelected = false;
            dealingDamage = false;
            dealingDamageCounter = 0;

            isDead = false;
            isDying = false;
            isZombie = false;
            inBuilding = false;
            isAttacking = false;
            actionSelected = false;
            actionReady = false;
            rand = new Random();

            actions = new List<Action>();
            currentActionIndex = 0;
            actions.Add(new Move(false, 0, content.Load<Texture2D>("Content\\Txtr\\Units\\move")));


            targetReached = true;
            direction = new Vector2(1, 0);


            this.texture = content.Load<Texture2D>("Content\\Txtr\\Units\\Sprites\\civ_civilian_sheet");
            this.hudTexture = content.Load<Texture2D>("Content\\Txtr\\Units\\HUD\\copHUD");
            this.selectedCircle = content.Load<Texture2D>("Content\\Txtr\\Units\\selectedUnitCircle");
            this.emptyBar = content.Load<Texture2D>("Content\\Txtr\\Units\\EmptyBar");
            this.fullBar = content.Load<Texture2D>("Content\\Txtr\\Units\\FullBarWhite");
            frameCount = 10;
            frameIncrementer = 1;
            weaponFrameIncrementer = 1;
            weaponFrameCount = 0;
            dyingFrameCount = 0;
            groupOffset = new Vector2(0, 0);
            this.content = content;
        }


        public virtual void Update(GameTime gameTime, List<Unit> enemiesInRange, List<Unit> friendsInRange)
        {



            //Check Recovery Time from actions
            TimeSpan elapsed = gameTime.ElapsedGameTime;

            recoveryTime -= elapsed.Milliseconds / 1000.0f;


            if (recoveryTime <= 0)
            {
                recoveryTime = 0;
                actionReady = true;
            }

            if (inBuilding == false)
            {


                //Attack if enemy target is in range
                if (enemiesInRange.Count > 0)
                {
                    isAttacking = true;

                    if (actionReady == true)
                    {
                        int atkIndex = rand.Next(enemiesInRange.Count - 1);

                        this.DealDamage(enemiesInRange[atkIndex]);

                        dealingDamage = true;
                        dealingDamageCounter = 60;
                        zTarget = enemiesInRange[atkIndex];
                        
                        tempDirection = enemiesInRange[atkIndex].Position - position;
                        tempDirection.Normalize();

                        


                        recoveryTime = weapon.RecoveryTime;
                        initialRecoveryTime = weapon.RecoveryTime;

                        actionReady = false;


                    }

                }
                else
                {
                    isAttacking = false;
                }


                //Move if target is not reached
                if (!targetReached && isAttacking == false)
                {
                    position += velocity * direction;
                }

                //If Target Reached, Set Next Target or Stop
                if (Math.Abs(position.X - targetPosition.X) < velocity
                    && Math.Abs(position.Y - targetPosition.Y) < velocity)
                {
                    targetReached = true;

                    if (pathIndex > 0 && path != null)
                    {
                        pathIndex--;
                        targetPosition = path.GetPath[pathIndex].Position + groupOffset;
                        direction = targetPosition - position;
                        direction.Normalize();
                        targetReached = false;
                    }
                }

            }

        }

        public void Draw(SpriteBatch spritebatch, int mapFocusX, int mapFocusY)
        {
            if (inBuilding == false)
            {
                Color hpBar;

                if (isZombie == false)
                {
                    hpBar = Color.Green;
                }
                else
                {
                    hpBar = Color.Purple;
                }

                int hpBarLength = (int)(((float)hitPoints / (float)maxHitPoints) * Tile.TILEWIDTH);

                if (hitPoints < ((maxHitPoints * 2) / 3))
                {
                    if (isZombie == false)
                    {
                        hpBar = Color.Yellow;
                    }
                    else
                    {
                        hpBar = Color.RosyBrown;
                    }
                }

                if (hitPoints < (maxHitPoints / 3))
                {
                    if (isZombie == false)
                    {
                        hpBar = Color.Red;
                    }
                    else
                    {
                        hpBar = Color.DarkBlue;
                    }
                }

                if (isDying == false && isDead == false)
                {
                    if ((!isZombie) || ((isZombie) && (SolanumSolstice.showEnemyHP)))
                    {
                        spritebatch.Draw(emptyBar, new Rectangle((int)position.X - mapFocusX, (int)position.Y - mapFocusY - 3, 32, 7), Color.White);
                        spritebatch.Draw(fullBar, new Rectangle((int)position.X - mapFocusX, (int)position.Y - mapFocusY - 3, hpBarLength, 7), hpBar);
                    }
                }



                if (isSelected == true && isDead == false && isDying == false)
                {
                    Color color = Color.White;

                    if (actionSelected == true)
                    {
                        color = Color.Yellow;
                    }

                    spritebatch.Draw(selectedCircle, new Rectangle((int)position.X - mapFocusX, (int)position.Y - mapFocusY, 32, 32), color);

                }




                Color personColor = Color.White;


                if (isDying == true)
                {
                    Rectangle dyingFrame = GetDyingFrame();

                    spritebatch.Draw(texture, new Rectangle((int)position.X - mapFocusX, (int)position.Y - mapFocusY, 32, 32), dyingFrame, Color.White);
                }
                else if (isDead == true && isDying == false)
                {
                    spritebatch.Draw(texture, new Rectangle((int)position.X - mapFocusX, (int)position.Y - mapFocusY, 32, 32), GetDeathFrame(), Color.White);
                }
                else
                {

                    Rectangle frame = GetCurrentFrame();

                    if (isZombie == true && isAttacking == true)
                    {
                        frame.Y += 256;
                    }

                    spritebatch.Draw(texture, new Rectangle((int)position.X - mapFocusX, (int)position.Y - mapFocusY, 32, 32), frame, personColor);
                    if (isZombie == false)
                    {
                        Rectangle weaponFrame = GetCurrentWeaponFrame();
                        if (dealingDamage == true)
                        {
                            dealingDamageCounter--;
                            weaponFrame = new Rectangle(32, weaponFrame.Y + 128, weaponFrame.Width, weaponFrame.Height);
                            spritebatch.Draw(weapon.Texture, new Rectangle((int)zTarget.Position.X - mapFocusX, (int)zTarget.Position.Y - mapFocusY, 32, 32), new Rectangle(dealingDamageCounter % 3, 256, 32, 32), Color.White);
                            spritebatch.Draw(weapon.Texture, new Rectangle((int)position.X - mapFocusX, (int)position.Y - mapFocusY, 32, 32), weaponFrame, Color.Yellow);
                            if (dealingDamageCounter == 0)
                            {
                                dealingDamage = false;
                            }
                        }
                        else
                        {
                            spritebatch.Draw(weapon.Texture, new Rectangle((int)position.X - mapFocusX, (int)position.Y - mapFocusY, 32, 32), weaponFrame, Color.Yellow);
                        }
                    }
                }

            }
        }

        public Rectangle GetDeathFrame()
        {
            float slope;

            if (direction.X != 0.0f)
            {
                slope = (direction.Y / direction.X);
            }
            else if (direction.X == 0 && direction.Y > 0)
            {
                return (new Rectangle(0, 64 + 160, 32, 32));
            }
            else
            {
                return (new Rectangle(0, 0 + 160, 32, 32));
            }

            //Down
            if (Math.Abs(slope) >= 1.0f && direction.Y < 0.0f)
            {
                return (new Rectangle(0, 0 + 160, 32, 32));
            }
            //Right
            else if (Math.Abs(slope) < 1.0f && direction.X > 0.0f)
            {
                return (new Rectangle(0, 64 + 160, 32, 32));
            }
            //Up
            else if (Math.Abs(slope) >= 1.0f && direction.Y > 0.0f)
            {
                return (new Rectangle(0, 64 + 160, 32, 32));
            }
            //Left
            else if (Math.Abs(slope) < 1.0f && slope <= 0.0f)
            {
                return (new Rectangle(0, 0 + 160, 32, 32));
            }
            else
            {
                return (new Rectangle(0, 0 + 160, 32, 32));
            }
        }

        public Rectangle GetDyingFrame()
        {
            dyingFrameCount++;


            if (dyingFrameCount > 44)
            {
                isDying = false;
            }


            float slope;

            if (direction.X != 0.0f)
            {
                slope = (direction.Y / direction.X);
            }
            else if (direction.X == 0 && direction.Y > 0)
            {
                return (new Rectangle(32 * (dyingFrameCount / 15), 0 + 128, 32, 32));
            }
            else
            {
                return (new Rectangle(32 * (dyingFrameCount / 15), 64 + 128, 32, 32));
            }

            //Down
            if (Math.Abs(slope) >= 1.0f && direction.Y < 0.0f)
            {
                return (new Rectangle(32 * (dyingFrameCount / 15), 0 + 128, 32, 32));
            }
            //Right
            else if (Math.Abs(slope) < 1.0f && direction.X > 0.0f)
            {
                return (new Rectangle(32 * (dyingFrameCount / 15), 64 + 128, 32, 32));
            }
            //Up
            else if (Math.Abs(slope) >= 1.0f && direction.Y > 0.0f)
            {
                return (new Rectangle(32 * (dyingFrameCount / 15), 64 + 128, 32, 32));
            }
            //Left
            else if (Math.Abs(slope) < 1.0f && slope <= 0.0f)
            {
                return (new Rectangle(32 * (dyingFrameCount / 15), 0 + 128, 32, 32));
            }
            else
            {
                return (new Rectangle(32 * (dyingFrameCount / 15), 0 + 128, 32, 32));
            }
        }

        public Rectangle GetCurrentWeaponFrame()
        {
            if (targetReached == false)
            {
                weaponFrameCount += 1 * weaponFrameIncrementer;
            }

            if (weaponFrameCount >= 29)
            {
                weaponFrameIncrementer = -1;

            }

            if (weaponFrameCount <= 0)
            {
                weaponFrameIncrementer = 1;

            }

            float slope;

            if (direction.X != 0.0f)
            {
                slope = (direction.Y / direction.X);
            }
            else if (direction.X == 0 && direction.Y > 0)
            {
                return (new Rectangle(32 * (weaponFrameCount / 10), 0, 32, 32));
            }
            else
            {
                return (new Rectangle(32 * (weaponFrameCount / 10), 64, 32, 32));
            }

            //Down
            if (Math.Abs(slope) >= 1.0f && direction.Y < 0.0f)
            {
                return (new Rectangle(32 * (weaponFrameCount / 10), 0, 32, 32));
            }
            //Right
            else if (Math.Abs(slope) < 1.0f && direction.X > 0.0f)
            {
                return (new Rectangle(32 * (weaponFrameCount / 10), 32, 32, 32));
            }
            //Up
            else if (Math.Abs(slope) >= 1.0f && direction.Y > 0.0f)
            {
                return (new Rectangle(32 * (weaponFrameCount / 10), 64, 32, 32));
            }
            //Left
            else if (Math.Abs(slope) < 1.0f && slope <= 0.0f)
            {
                return (new Rectangle(32 * (weaponFrameCount / 10), 96, 32, 32));
            }
            else
            {
                return (new Rectangle(32 * (weaponFrameCount / 10), 0, 32, 32));
            }
        }

        public Rectangle GetCurrentFrame()
        {

            if (targetReached == false && isAttacking == false)
            {
                frameCount += 1 * frameIncrementer;
            }
            else if (isAttacking == true && isZombie == true)
            {
                frameCount += 1 * frameIncrementer;
            }

            if (frameCount >= 29)
            {
                frameIncrementer = -1;

            }

            if (frameCount <= 0)
            {
                frameIncrementer = 1;

            }

            float slope;

            Vector2 direction = this.direction;

            if (isAttacking)
            {
                direction = tempDirection;
            }

            if (direction.X != 0.0f)
            {
                slope = (direction.Y / direction.X);
            }
            else if (direction.X == 0 && direction.Y > 0)
            {
                return (new Rectangle(32 * (frameCount / 10), 0, 32, 32));
            }
            else
            {
                return (new Rectangle(32 * (frameCount / 10), 64, 32, 32));
            }

            //Down
            if (Math.Abs(slope) >= 1.0f && direction.Y < 0.0f)
            {
                return (new Rectangle(32 * (frameCount / 10), 0, 32, 32));
            }
            //Right
            else if (Math.Abs(slope) < 1.0f && direction.X > 0.0f)
            {
                return (new Rectangle(32 * (frameCount / 10), 32, 32, 32));
            }
            //Up
            else if (Math.Abs(slope) >= 1.0f && direction.Y > 0.0f)
            {
                return (new Rectangle(32 * (frameCount / 10), 64, 32, 32));
            }
            //Left
            else if (Math.Abs(slope) < 1.0f && direction.X < 0.0f)
            {
                return (new Rectangle(32 * (frameCount / 10), 96, 32, 32));
            }
            else
            {
                return (new Rectangle(32 * (frameCount / 10), 0, 32, 32));
            }
        }

        public void PerformAction(Vector2 targetPos, Unit targetUnit)
        {
            targetReached = true;
            actionReady = false;
            recoveryTime = actions[currentActionIndex].RecoveryTime;
            actions[currentActionIndex].PerformAction(this, targetPos, targetUnit);

            currentActionIndex = 0;
        }

        public virtual void SetTarget(Vector2 potentialTarget)
        {
            if (potentialTarget != finalTarget)
            {
                path = Map.GetShortestPath(position, potentialTarget);

                if (path != null)
                {
                    int pathCount = path.GetPath.Count - 1;

                    if (path.GetPath.Count >= 2)
                    {
                        pathCount--;
                    }

                    finalTarget = path.GetPath[0].Position + groupOffset;

                    targetPosition = path.GetPath[pathCount].Position + groupOffset;
                    pathIndex = pathCount;
                    direction = targetPosition - position;

                    if (direction.X != 0 || direction.Y != 0)
                    {
                        direction.Normalize();
                    }

                    targetReached = false;
                }
                else
                {
                    targetReached = true;
                }
            }

        }


        public void DealDamage(Unit unit)
        {
            if (inBuilding == false)
            {
                SolanumSolstice.soundBank.PlayCue(weapon.WeaponSound);



            }
            unit.TakeDamage(rand.Next(Weapon.AttackPower) + attackPower);

        }

        public void DealDamage(Unit unit, int amount)
        {
            unit.TakeDamage(amount);
        }

        public void DealDamage(Unit unit, float multiplier)
        {
            unit.TakeDamage((rand.Next(Weapon.AttackPower) + attackPower) * 2);
        }

        public void TakeDamage(int damage)
        {
            hitPoints -= damage;

            if (hitPoints <= 0)
            {
                isDying = true;
                isDead = true;
                if (isZombie == false)
                {
                    SolanumSolstice.soundBank.PlayCue("scream_pain");
                }
                else if (isZombie == true && inBuilding == false)
                {
                    int sound = rand.Next(4);

                    if (sound == 1)
                    {
                        SolanumSolstice.soundBank.PlayCue("zombiedeath1");
                    }
                    else if (sound == 2)
                    {
                        SolanumSolstice.soundBank.PlayCue("zombiedeath2");
                    }
                    else
                    {
                        SolanumSolstice.soundBank.PlayCue("zombiedeath3");
                    }

                }
            }
        }

        public void HealDamage(int damage)
        {
            hitPoints += damage;

            if (hitPoints >= maxHitPoints)
            {
                hitPoints = maxHitPoints;
            }
        }

    }
}


